Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

move-on

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

move-on

Creates a queue of sync or async functions with resolve and reject callback.

  • 1.1.2
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Description

move-on is a very light module that creates a chain of synchronous or asynchronous functions and sets its own inner userContext object for this keyword for all chained functions.

  • move-on module function executes the functions sequentially in the chain.
    The done function is called when the last function from the chain is resolved.

  • move-on all static method executes all the functions simultaneously.
    The done function is called when all functions from the chain are resolved.

  • Any bugs found? Give me to know on dev.rafalko@gmail.com or on GitHub

Installation

npm install move-on

const moveOn = require('move-on');
const functionList = [funA, funB];
const userContext = {};

moveOn(functionList, userContext, onDone, onError);

function funA(resolve,reject){
  resolve();
}

function funB(resolve,reject){
  resolve();
}

function onDone(userContext,reject){
  console.log('all functions succeeded!');
  //continuation code here
}

function onError(userContext,msg){
  console.error(msg);
}

Usage

moveOn(list, userContext, done, catch)

moveOn.all(list, userContext, done, catch)

list [Array]
  • The [Array] list should contain the [Function] items that will be chained and executed back-to-back.
  • Avoid using (or use wisely) arrow functions. They cannot be binded, so the userContext will not be set for them.
userContext [Object|null]
  • All [Function] items are automatically binded to the userContext Object.
  • Each [Function] item has the access to the userContext Object by this keyword.
  • The [Object] userContext properties can be set and get in any queued function, thereby shared between each function of the queue block.
  • Pass an empty object {} to create the new userContect for the chain.
  • In order not to bind the [Function] items (to keep the current this reference), set userContext to null
done(userContext,reject) [Function]
  • The [Function] done is automatically attached at the end of the chain and called as the last one (if all previous functions have been resolved). [sample]
  • Arguments passed through done:
    • [0] [Object] userContext for this; It is useful if you define done function as an arrow function (arrow functions are not intended to be binded by default, so the access to the userContext this object can be gained only via the argument).
    • [1] [Function] reject function; You can still reject the chain in the done function by calling reject().
catch(userContext) [Function]
  • The [Function] catch is automatically attached at the end of the chain as catch() function and executed in case of the rejection of any queued function (when the reject() is called).
  • Arguments passed through catch:
    • [0] [Object] userContext for this; It is useful if you define catch function as an arrow function (arrow functions are not intended to be binded by default, so the access to the userContext this object can be gained only via the argument).

Chained functions

  • The [Function] resolve and [Function] reject arguments are passed through each chained [Function] item.
  • When resolve is called, the further chained [Function] item is called.
  • When reject is called, the catch callback function is called. [see below]
  • Both resolve and reject can be called with any number of arguments. They will be passed to the further function in the chain, when resolved, or to the catch function, when rejected. [see below]
function fetchData(resolve,reject){
  if(foo) {
    resolve(foo);
  } else {
    reject('foo is not defined.');
  }
}

Behaviour

  • Mind that both resolve and reject do not end function execution. In order to end function execution, use return resolve() or return reject(). [sample]
  • The resolve and reject can be called many times in the same function:
    • when you call resolve callback function twice, it runs the further part of the chain twice. You can, for example, call the resolve function once, then redefine the properties in the userContext this object, then call resolve again.
    • you can also call resolve in the for loop. [sample]
    • you can call reject twice for example to pass two different error messages to the catch function. [sample]
  • You can also put one function chain into another and thereby create forks of chains [sample]

Samples

The sample of sync and async functions used in the chain.
const moveOn = require('move-on');

//set the list of functions in order
const functionList = [setInitialData,getName,getAge];
//set the userContext accessible via this keyword
const userContext = {};

moveOn(functionList, userContext, onDone, onCatch);

function onDone(userContext,reject){
  console.log(`The new employee of ${this.company} company, based in ${this.city}, is ${this.age}yo ${this.name}.`);
  //=> The new employee of PierogSoft company, based in Warsaw, is 26yo John Doe.
}

function onCatch(userContext,msg){
  console.log('Something went wrong: ',msg);
}

function setInitialData(resolve,reject){
  //this is a sample of synchronous function in the chain
  //this data will be accessible via this keyword in all chained functions
  this.company = 'PierogSoft';
  this.city = 'Warsaw';
  resolve();
}

function getName(resolve,reject){
  //this is a sample of asynchronous function in the chain
  //it runs resolve() when it's ready
  setTimeout(()=>{
    //mind that arrow function takes userContext this from the parent function
    this.name = 'John Doe';
    resolve();
  },1000);
}

function getAge(resolve,reject){
  //this is another sample of asynchronous function in the chain
  //it runs resolve() when it's ready
  setTimeout(()=>{
    //mind that arrow function takes userContext this from the parent function
    this.age = 26;
    resolve();
  },600);
}
The userContext object is defined with some initial properties.
The arguments can be passed through resolve and reject functions.
const moveOn = require('move-on');

//set userContext object with some initial properties
const userContext = {
  name:'John Doe',
  age:25
};

moveOn([check], userContext, onDone, onCatch);

function check(resolve,reject){
  //check if initial properties are accessible
  console.log(this.name); //John Doe
  console.log(this.age); //25
  if(this.name&&this.age){
    //pass the argument to the further function
    resolve('person');
  } else {
    //pass the argument to the catch function
    reject('The person was not defined.');
  }
}

function onDone(userContext,reject,isPerson){
  //get the 'isPerson' argument pass in the previous function
  if(isPerson==='person') console.log(`New person: ${this.name} (${this.age}yo).`);
}

function onCatch(userContext,msg){
  console.log('Something went wrong: ',msg);
}
Mind that the rejected function terminates the execution of the further part of the chain and executes catch function immediately.
const moveOn = require('./index.js');

moveOn([a,b,c], {}, onDone, onCatch);

function a(resolve,reject){
  resolve();
}
function b(resolve,reject){
  reject('oops!');
}
function c(resolve,reject){
  //it's been never called!
  resolve();
}

function onDone(userContext,reject){
  //it's been never called!
}

function onCatch(userContext,msg){
  console.log(msg); //oops!
}
Mind how return resolve() ends the function.
See how the accessibility of properties defined after resolve() works in the further functions.
const moveOn = require('move-on');

moveOn([a,b,c], {}, onDone, onCatch);

function a(resolve){
  resolve();
  console.log('this is still logged!');
  //lets define some properties after resolve() call
  this.name = 'John Doe';
  this.age = 25;
}
function b(resolve){
  //properties are inaccessible because they were defined after this function was executed
  console.log(this.name); //undefined
  console.log(this.age); //undefined
  return resolve();
  console.log('this is nog logged');
}
function c(resolve){
  setTimeout(()=>{
    //properties are accessible because c function works asynchronously
    console.log(this.name); //John Doe
    console.log(this.age); //25
    resolve();
  },500);
}

function onDone(userContext,reject){
  //properties are accessible because onDone function is fired after async c function is resolved
  console.log(this.name); //John Doe
  console.log(this.age); //25
}

function onCatch(userContext){
  //it's been never called
}
Chain can be forked!
Mind that the inner chain gets its parent this as the userContext object.
const moveOn = require('move-on');

moveOn([a,b,c], {}, onDone, onCatch);

/* Order of executions:
 * a
 * x
 * y
 * z
 * b
 * c
 */

function a(resolve){
  resolve();
}
function b(resolve){
  //lets define some properties
  this.name = 'John Doe';
  this.age = 25;
  
  //set this as the userContext of the inner queue
  moveOn([x,y,z], this, function(){
    resolve(); //resolve b after x, y and z are succeeded!
  }, onCatch); //one onCatch function for all rejected functions
}
function c(resolve){
  resolve();
}

function x(resolve){
  //check if the parent userContext is accessible
  console.log(this.name); //John Doe
  resolve();
}

function y(resolve){
  resolve();
}

function z(resolve){
  resolve();
}

function onDone(userContext,reject){
  console.log('Success!');
}

function onCatch(userContext){
  //it's been never called
}

Notice how the instructions placed before and after resolve() behave.
You can move on to the last function, then set some final data, then get back to the initial function.
const moveOn = require('move-on');
moveOn([a,b,c], {}, onDone, onCatch);

/* Order of executions:
a before
x before
y before
z before
b before
c before
DONE!
c after
b after
z after
y after
x after
a after
 */

function a(resolve){
  console.log('a before');
  resolve();
  console.log('a after');
}
function b(resolve){
  moveOn([x,y,z], this, function(){
    console.log('b before');
    resolve();
    console.log('b after');
  }, onCatch);
}
function c(resolve){
  console.log('c before');
  resolve();
  console.log('c after');
}

function x(resolve){
  console.log('x before');
  resolve();
  console.log('x after');
}

function y(resolve){
  console.log('y before');
  resolve();
  console.log('y after');
}

function z(resolve){
  console.log('z before');
  resolve();
  console.log('z after');
}

function onDone(userContext,reject){
  console.log('DONE!');
}

function onCatch(userContext){
  //it's been never called
}

A part of the chain is ran in the loop.
const moveOn = require('./index.js');

const productsData = {
  id: ['ab12','xy34'],
  products:[]
};

moveOn([init,findPrice,isAvailable], productsData, onDone, onCatch);

function init(resolve){
  this.iter = 0; //set iter to find out when all async functions are done
  for(let i in this.id){
    //run findPrice() and isAvailable() back-to-back for each ID
    resolve(this.id[i]);
  }
}

function findPrice(resolve,reject,getID){
  setTimeout(()=>{
    //assume that we asynchronously fetch the price due to the ID of the product
    var price = Math.round(Math.random()*100)+'$';
    resolve(getID,price);
  },500);
}

function isAvailable(resolve,reject,getID,getPrice){
  setTimeout(()=>{
    //assume that we check if the product of specified ID is available
    var available = Math.random()>.5;
    //save the data about the product
    this.products.push({id:getID,price:getPrice,available:available});
    this.iter++;
    //if this is the last async function, resolve() and finish the chain
    if(this.iter===this.id.length) resolve();
  },500);
}

function onDone(userContext,reject){
  console.log('Success!');
  console.log(this.products);
  //{ id: 'ab12', price: '71$', available: true },
  //{ id: 'xy34', price: '31$', available: false }
}

function onCatch(userContext){
  //it's been never called
}
See how the multiple reject() execution can be used for a validation.
const moveOn = require('move-on');

const validFunctions = [
  isString,
  hasAtSign,
  hasMultipleAtSigns,
  hasSpecialChars,
  hasSpaces,
  hasDoubleDots,
  isAvailable
];

const address = {
  email: 'John..doe@gm@ail.com'
};

moveOn(validFunctions, address, onDone, onCatch);

/* console =>
 * Invalid: The value can contain only one @ sign.
 * Invalid: The value cannot contain capital letters.
 * Invalid: The value cannot contain double dots.
 * Invalid: The John..doe@gm@ail.com address is unavailable.
 */

function isString(resolve,reject){
  if(typeof this.email !=='string') return reject('The value must be a String.');
  resolve();
}

function hasAtSign(resolve,reject){
  if(!(/@/).test(this.email)) reject('The value must contain @ sign.');
  resolve();
}

function hasMultipleAtSigns(resolve,reject){
  if((/@.*@/).test(this.email)) reject('The value can contain only one @ sign.');
  resolve();
}

function hasSpecialChars(resolve,reject){
  if(!(/^[a-z.\-@_]+$/).test(this.email)) reject('The value cannot contain capital letters.');
  resolve();
}

function hasCapitals(resolve,reject){
  if((/[A-Z]/).test(this.email)) reject('The value cannot contain special characters.');
  resolve();
}

function hasSpaces(resolve,reject){
  if((/\s/).test(this.email)) reject('The value cannot contain spaces.');
  resolve();
}

function hasDoubleDots(resolve,reject){
  if((/\.{2,}/).test(this.email)) reject('The value cannot contain double dots.');
  resolve();
}

function isAvailable(resolve,reject){
  //this.atLeastOneErr is set in the onCatch() function
  //if the address is invalid, it makes no sense to check if is available
  if(this.atLeastOneErr) return;
  setTimeout(()=>{
    var available = Math.random()>.5;
    if(available) resolve(`Success! The ${this.email} address is available!`);
    if(!available) reject(`The ${this.email} address is unavailable.`);
  },234);
}

function onDone(userContext,reject,msg){
  console.log(msg);
}

function onCatch(userContext,msg){
  this.atLeastOneErr = true;
  console.log('Invalid: ' + msg);
}

Keywords

FAQs

Package last updated on 18 Jun 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc